﻿using eslib.CRClib;
using System;
using System.Collections.Generic;

namespace eslib.nnp5
{
    /// <summary>
    /// 寄存器读写式数据包（基本数据包）        
    /// 数据包结构:  (小端字节序)
    /// [Addr(2Bytes)]          --广播地址为0xffff.
    /// [Cmd(2Bytes)]   
    /// [DataLen(2Bytes)]   
    /// [Data((DataLen)Bytes)]      --长度等于DataLen
    /// [CRC(4B)]       --32位CRC校验值,范围[Addr,Data]
    /// </summary>
    public class RRWPackage : IPackage
    {
        public byte[] GetBuffer()
        {
            return Buffer;
        }

        /// <summary>
        /// 数据缓存
        /// </summary>
        public byte[] Buffer { get; private set; }


        /// <summary>
        /// 构造数据包，自动增加crc校验
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="cmd"></param>
        /// <param name="data"></param>
        public RRWPackage(ushort addr, ushort cmd, byte[] data)
        {
            Addr = addr;
            Cmd = cmd;
            Data = data;
            DataLen = (UInt16)data.Length;

            //计算CRC
            updateCRC();

            //更新缓存
            makeBuffer();
        }

        private RRWPackage() { }


        /// <summary>
        /// 转换数据包,带crc验证
        /// </summary>
        /// <param name="buffer">缓存，不包括包头包尾</param>
        /// <returns></returns>
        public static (bool success, RRWPackage pkg) TransferPkg(byte[] buffer)
        {
            try
            {
                RRWPackage pkg = new RRWPackage();
                int p = 0;

                pkg.Addr = BitConverter.ToUInt16(buffer, p);
                p += 2;

                pkg.Cmd = BitConverter.ToUInt16(buffer, p);
                p += 2;

                pkg.DataLen = BitConverter.ToUInt16(buffer, p);
                p += 2;

                //长度验证
                int dlen = 6 + pkg.DataLen + 4;
                if (buffer.Length != dlen) return (false, null);

                //data
                pkg.Data = new byte[pkg.DataLen];
                for (int i = 0; i < pkg.DataLen; i++)
                    pkg.Data[i] = buffer[p++];

                //crc
                pkg.CRC = new byte[4];
                for (int i = 0; i < 4; i++)
                    pkg.CRC[i] = buffer[p++];

                //复制到缓存
                pkg.Buffer = new byte[dlen];
                for (int i = 0; i < buffer.Length; i++)
                    pkg.Buffer[i] = buffer[i];

                //检查crc
                if (!pkg.crcCheck()) return (false, null);

                return (true, pkg);
            }
            catch
            {
                return (false, null);
            }
        }



        private void makeBuffer()
        {
            List<byte> list = new List<byte>();

            //数据部分
            list.AddRange(getComputeCRCData());

            //crc
            list.AddRange(CRC);

            Buffer = list.ToArray();
        }


        /// <summary>
        /// 目标地址
        /// </summary>
        public UInt16 Addr { get; set; }

        /// <summary>
        /// 广播地址
        /// </summary>
        public static UInt16 BroadcastAddr => 0xffff;


        /// <summary>
        /// 指令
        /// </summary>
        public UInt16 Cmd { get; set; }

        private UInt16 DataLen { get; set; }

        /// <summary>
        /// 数据段
        /// </summary>
        public Byte[] Data { get; private set; }

        public Byte[] CRC { get; private set; }



        /// <summary>
        /// 获取要计算CRC的数据
        /// </summary>
        /// <returns></returns>
        private byte[] getComputeCRCData()
        {
            List<byte> list = new List<byte>();

            list.AddRange(BitConverter.GetBytes(Addr));
            list.AddRange(BitConverter.GetBytes(Cmd));
            list.AddRange(BitConverter.GetBytes(DataLen));
            list.AddRange(Data);

            return list.ToArray();
        }


        /// <summary>
        /// 检查CRC，正确返回true
        /// </summary>
        /// <returns></returns>
        private bool crcCheck()
        {
            EsCrc escrc = new EsCrc();
            uint dataCRC = escrc.ComputeCrc(getComputeCRCData());       //数据段crc
            uint originCRC = BitConverter.ToUInt32(this.CRC, 0);        //原始CRC
            return dataCRC == originCRC;
        }


        /// <summary>
        /// 更新CRC
        /// </summary>
        private void updateCRC()
        {
            EsCrc escrc = new EsCrc();
            this.CRC = EsCrc.Uint2Bytes(escrc.ComputeCrc(getComputeCRCData()));
        }

    }
}
